Skip to content

feat: Add connection-level query timeout support#5

Open
rovshan-b wants to merge 2 commits intomainfrom
feature/arrow-support-query-timeouts
Open

feat: Add connection-level query timeout support#5
rovshan-b wants to merge 2 commits intomainfrom
feature/arrow-support-query-timeouts

Conversation

@rovshan-b
Copy link
Copy Markdown

Query Timeout Support via x-query-timeout-ms gRPC Header

This change adds support for propagating query timeouts to the IOMETE Flight SQL server using a custom x-query-timeout-ms gRPC header. When a timeout is configured, the header is attached to every query execution request, allowing the server to enforce it server-side.

Statement-level timeout

Use the standard JDBC API on any Statement or PreparedStatement:

Statement stmt = connection.createStatement();
stmt.setQueryTimeout(30); // seconds
stmt.executeQuery("SELECT ...");

The header x-query-timeout-ms: 30000 will be sent with the request. A value of 0 means no timeout (default).

Connection-level (global) timeout

Set a default timeout for all queries on a connection via the JDBC URL or Properties:

jdbc:arrow-flight-sql://host:port?queryTimeout=60
Properties props = new Properties();
props.setProperty("queryTimeout", "60"); // seconds
Connection conn = DriverManager.getConnection(url, props);

All statements on this connection will include x-query-timeout-ms: 60000 unless overridden at the statement level.

Precedence

Statement-level timeout takes priority over the connection-level default. If setQueryTimeout(0) is called on a statement, it falls back to the connection-level value. If neither is set, no header is sent.

Configuration Header sent
stmt.setQueryTimeout(30) x-query-timeout-ms: 30000
?queryTimeout=60 (no statement timeout) x-query-timeout-ms: 60000
stmt.setQueryTimeout(30) + ?queryTimeout=60 x-query-timeout-ms: 30000
Neither configured (no header)

@rovshan-b rovshan-b changed the title feat: Add connection-level query timeout support and related tests feat: Add connection-level query timeout support Apr 27, 2026
Copy link
Copy Markdown
Collaborator

@mateusaubin mateusaubin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: logic is scattered in multiple files, making further maintenance (rebase from upstream) hard.

please group all logic into a single place such that we can keep it isolated from upstream, my recommendation:

/** Utility for building query timeout call options. */
public final class QueryTimeoutCallOption implements CallOption {
  private QueryTimeoutCallOption() {}

    /**
     * Merges base call options with extra options.
     *
     * @param baseOptions the base options
     * @param extraOptions additional options to merge
     * @return combined array of options
     */
    public static CallOption[] mergeOptions(CallOption[] baseOptions, CallOption... extraOptions) {
        if (extraOptions.length == 0) {
            return baseOptions;
        }
        final CallOption[] combined = new CallOption[baseOptions.length + extraOptions.length];
        System.arraycopy(baseOptions, 0, combined, 0, baseOptions.length);
        System.arraycopy(extraOptions, 0, combined, baseOptions.length, extraOptions.length);
        return combined;
    }

  /**
   * Builds a CallOption array from a statement's timeout configuration.
   *
   * @param statement the statement to extract timeouts from
   * @return CallOption array with timeout header, or empty array if no timeout configured
   * @throws SQLException on error retrieving timeout values
   */
  public static CallOption[] fromStatement(ArrowFlightInfoStatement statement)
      throws SQLException {
    final Duration statementTimeout = Duration.ofSeconds(statement.getQueryTimeout());
    final Duration connectionTimeout =
        Duration.ofSeconds(statement.getConnection().getConnectionQueryTimeoutSeconds());
    return build(statementTimeout, connectionTimeout);
  }

  /**
   * Builds a CallOption array from a statement ID and connection.
   *
   * @param statementId the statement ID to look up in the connection's statement map
   * @param connection the Avatica connection to extract timeouts from
   * @return CallOption array with timeout header, or empty array if no timeout configured
   */
  public static CallOption[] fromStatementHandle(int statementId, AvaticaConnection connection) {
    Duration statementTimeout = Duration.ZERO;
    final AvaticaStatement stmt = connection.statementMap.get(statementId);
    if (stmt != null) {
      try {
        statementTimeout = Duration.ofSeconds(stmt.getQueryTimeout());
      } catch (SQLException ignored) {
        // fall through to connection-level timeout
      }
    }
    Duration connectionTimeout = Duration.ZERO;
    if (connection instanceof ArrowFlightConnection) {
      connectionTimeout =
          Duration.ofSeconds(
              ((ArrowFlightConnection) connection).getConnectionQueryTimeoutSeconds());
    }
    return build(statementTimeout, connectionTimeout);
  }

  /**
   * Builds a CallOption array containing query timeout header if a timeout is configured.
   *
   * @param statementTimeout timeout at statement level (ZERO means use connection-level)
   * @param connectionTimeout timeout at connection level
   * @return CallOption array with timeout header, or empty array if no timeout configured
   */
  private static CallOption[] build(Duration statementTimeout, Duration connectionTimeout) {
    final Duration effectiveTimeout =
        !statementTimeout.isZero() ? statementTimeout : connectionTimeout;

      if (effectiveTimeout.isZero()) {
          return new CallOption[0]; // short-circuit
      }

      final FlightCallHeaders headers = new FlightCallHeaders();
      headers.insert("x-query-timeout-ms", String.valueOf(effectiveTimeout.toMillis()));
      return new CallOption[] {new HeaderCallOption(headers)};
  }
}

Comment thread .gitignore Outdated
@rovshan-b rovshan-b requested a review from mateusaubin May 11, 2026 11:00
@rovshan-b
Copy link
Copy Markdown
Author

All comments handled. Kindly re-check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants